'''
Created on Nov 19, 2010

@author: dexen
'''

import time
import multiprocessing as mp
from multiprocessing.queues import Queue

from dexen.system.common.meta import JobMeta
from dexen.system.node.slave.comm import InUnit, OutUnit
from dexen.system.common.transact import SlaveRecvTransact
from dexen.system.node.slave.executor import SlaveTaskExecutor


PROCESS_SCHEDULE_TIMEOUT = 60 #seconds

#===============================================================================
# SlaveCore
#===============================================================================
class SlaveCore(mp.Process):
    def __init__(self, comm, slave_name):
        mp.Process.__init__(self)
        self.comm = comm
        self.executors = {} # job_id:SlaveTaskExecutor
        self.jobs = {} # job_id:JobMeta
        self.queues = {} # job_id : dict('in', 'out')
        self.last_exec_times = {} #job_id : time that was last executed
        self.slave_name = slave_name

    def _update_jobs(self):
        job_metas = self.comm.get_jobs()
        for job_meta in job_metas:
            assert isinstance(job_meta, JobMeta)
            job_id = job_meta.get_id()
            job_meta.setup_schema() 
            self.jobs[job_id] = job_meta

    def _update_executors(self):
        cur_time = time.time()
        for job_id, executor in self.executors.items():
            assert isinstance(executor, SlaveTaskExecutor)
            last_exec_time = self.last_exec_times[job_id]
            if cur_time - last_exec_time > PROCESS_SCHEDULE_TIMEOUT:
                executor.terminate()
                print "executor", job_id, "being terminated."
                time.sleep(0.1)
                del self.executors[job_id]
                del self.queues[job_id]
                
    def _create_executor(self, job_id):
        job_meta = self.jobs[job_id]
        assert isinstance(job_meta, JobMeta)
        inq, outq = mp.Queue(1), mp.Queue(1)
        self.queues[job_id] = {"in":inq, "out":outq}
        executor = SlaveTaskExecutor(job_meta, inq, outq)
        self.executors[job_id] = executor
        return executor

    def _get_executor(self, job_id):
        self.last_exec_times[job_id] = time.time()
        if self.executors.has_key(job_id):
            return self.executors[job_id]       
        executor = self._create_executor(job_id)
        executor.start()
        return executor
   
    def _update_transact(self):
        unit = self.comm.get_in_unit()
        if unit == None: return
        assert isinstance(unit, InUnit)
        job_id = unit.job_id
        executor = self._get_executor(job_id)    
        in_q = self.queues[job_id]["in"]
        out_q = self.queues[job_id]["out"]
        assert isinstance(in_q, Queue)
        assert isinstance(out_q, Queue)
        in_q.put(unit)
        transact = out_q.get()
        assert isinstance(transact, SlaveRecvTransact)
        print "%s executed %s"%(self.slave_name, transact.get_task_label()) 
        self.comm.set_out_unit(OutUnit(job_id, transact))
            
    def run(self):
        try:
            while True:
                self._update_jobs()
                self._update_transact()
                self._update_executors()            
                time.sleep(1)
        except KeyboardInterrupt:
            print "Cleaning up the job definition folders in slaves..."
            for _, job_meta in self.jobs.items():
                assert isinstance(job_meta, JobMeta)
                job_meta.teardown_schema()
        except:
            print "Slave core in exception"
                    
    
